from datetime import datetime

from sqlalchemy import DateTime, BigInteger
from sqlalchemy.event import listens_for
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column

from app.utils import context
from app.utils.snowflake import snowflake_id


class Base(DeclarativeBase):
    """
    SQLAlchemy基础声明类

    作为所有ORM模型的基类，提供基本的ORM映射功能
    """
    pass


class SuperEntityMixin:
    """
    SuperEntity混入类

    提供所有实体的基本字段，包括主键、创建时间、创建人等通用字段
    使用Mixin模式实现代码复用
    """
    # 主键
    id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=False)
    # 创建时间，可空字段
    create_time: Mapped[datetime | None] = mapped_column(DateTime)
    # 创建人，可空字段
    created_by: Mapped[int | None] = mapped_column(BigInteger)


class EntityMixin(SuperEntityMixin):
    """
    Entity混入类

    继承自SuperEntityMixin，额外提供更新时间、更新人等更新相关字段
    """
    # 修改时间，可空字段
    update_time: Mapped[datetime | None] = mapped_column(DateTime)
    # 修改人，可空字段
    updated_by: Mapped[int | None] = mapped_column(BigInteger)


"""
参考文档：
https://docs.sqlalchemy.org.cn/en/20/core/event.html#sqlalchemy.event.listens_for
"""


@listens_for(SuperEntityMixin, "before_insert", propagate=True)
def insert_fill(mapper, connection, target: SuperEntityMixin):
    """
    插入时对字段填充

    :param mapper: 映射器对象，包含实体映射信息
    :param connection: 数据库连接对象
    :param target: 实际要插入的目标实体实例
    :return:
    """
    # 如果id字段为空，则使用雪花算法生成唯一ID
    if target.id is None:
        target.id = snowflake_id.get_id()

    # 如果create_time字段为空，则填充当前时间
    if target.create_time is None:
        target.create_time = datetime.now()

    # 如果created_by字段为空，则从上下文中获取当前用户ID进行填充
    if target.created_by is None:
        target.created_by = context.get_user_id()

    # 如果update_time字段为空，则填充当前时间
    if target.update_time is None:
        target.update_time = datetime.now()

    # 如果updated_by字段为空，则从上下文中获取当前用户ID进行填充
    if target.updated_by is None:
        target.updated_by = context.get_user_id()


@listens_for(EntityMixin, "before_update", propagate=True)
def update_fill(mapper, connection, target: EntityMixin):
    """
    更新时对字段填充

    :param mapper: 映射器对象，包含实体映射信息
    :param connection: 数据库连接对象
    :param target: 实际要插入的目标实体实例
    :return: None
    """
    # 如果update_time字段为空，则填充当前时间
    if target.update_time is None:
        target.update_time = datetime.now()

    # 如果updated_by字段为空，则从上下文中获取当前用户ID进行填充
    if target.updated_by is None:
        target.updated_by = context.get_user_id()


